#include "include.c"

int initialize(void);
int print_help(char* notUsed);
int invalid_command(char* inputLine);
int close_program(char* notUsed);

bool	running = true;
char	commandNames[27][7] = {
			"lseek", "ls", "cd", "pwd", "mkdir", 
			"rmdir", "creat", "rm", "stat", "chmod",
			"open", "close", "read", "write", "pfd",
			"touch", "cat", "cp", "mv", "mount",
			"umount", "ps", "fork", "cs", "kill",
			"help", "exit" };
command	commands[28] = {
			&do_lseek, &do_ls, &do_cd, &do_pwd, &do_mkdir,
			&do_rmdir, &do_create, &do_rm, &do_stat, &do_chmod,
			&do_open, &do_close, &do_read, &do_write,
			&print_open_files, &do_touch, &do_cat, &do_cp, &do_mv,
			&do_mount, &do_umount, &do_ps, &do_fork, &do_cs, &do_kill,
			&print_help, &close_program, &invalid_command };

int main(int argc, char* argv[])
{	
	int i;
	char inputLine[MAX_PATH_LENGTH];
	
	if (initialize() == -1)
		return -1;
	
	getchar();
	do
	{
		printf("Process %d - input command: ", runningProc->pid);
		gets(inputLine);
		
		for (i = 0; i < 28; i++)
		{
			if ((i < 27) && (strncmp(commandNames[i], inputLine, strlen(commandNames[i])) == 0))
			{
				if (strchr(inputLine, ' ') == 0)
					(*commands[i])("");
				else
					(*commands[i])(inputLine + strlen(commandNames[i]) + 1);
				break;
			}
			else if (i == 27)
				(*commands[27])(inputLine);
		}
		memset(inputLine, '\0', MAX_PATH_LENGTH);
	} while(running);
	
	putchar('\n');
	return 0;
}

int initialize(void)
{
	int i, j;
	
	srand((unsigned)time(0));

	for (i = 0; i < NUM_MEM_INODES; i++)
		memInodes[i].refCount = 0;
		
	for (i = 0; i < NUM_MOUNTS; i++)
	{
		mountTable[i].busy = false;
		mountTable[i].mountedInode = 0;
	}
		
	for (i = 0; i < NUM_PROCS; i++)
	{
		procTable[i].status = PROC_FREE;
		procTable[i].next = &(procTable[i + 1]);
		for (j = 0; j < NUM_FILE_DESC; j++)
			procTable[i].files[j] = 0;
	}
	
	for (i = 0; i < NUM_OPEN_FILE; i++)
		openFiles[i].refCount = 0;
		
	printf("\nMounting root device...\n");
	if (mount_root_device() == -1)
		return -1;
	
	/* Create super user */
	runningProc = &procTable[0];
	runningProc->status = PROC_BUSY;
	runningProc->uid = 0;
	runningProc->gid = 0;
	runningProc->pid = 0;
	runningProc->ppid = 0;
	runningProc->parent = runningProc->sibling = runningProc;
	runningProc->child = 0;
	runningProc->next = &procTable[1];
	runningProc->cwd = rootInode;
	runningProc->cwd->refCount++;
	
	/* Create normal user */
	procTable[1].next = 0;
	procTable[1].parent = 0;
	procTable[1].sibling = runningProc;
	procTable[1].child = 0;
	procTable[1].status = PROC_READY;
	procTable[1].uid = 1;
	procTable[1].gid = 0;
	procTable[1].pid = 1;
	procTable[1].ppid = 0;
	procTable[1].cwd = rootInode;
	procTable[1].cwd->refCount++;
	
	numProcs = 2;
	
	return 1;
}

int print_help(char* notUsed)
{
	printf("\n************************************\n");
	printf(" Level 1:\n");
	printf("************************************\n");
	printf(" %-6s %-6s %-6s %-6s %-6s\n", "ls", "cd", "pwd", "creat", "rm");
	printf(" %-6s %-6s %-6s %-6s %-6s\n\n", "stat", "mkdir", "rmdir", "touch", "chmod");
	printf("************************************\n");
	printf(" Level 2:\n");
	printf("************************************\n");
	printf(" %-6s %-6s %-6s %-6s %-6s\n", "open", "close", "read", "write", "pfd");
	printf(" %-6s %-6s %-6s %-6s\n\n", "lseek", "cat", "cp", "mv");
	printf("************************************\n");
	printf(" Level 3:\n");
	printf("************************************\n");
	printf(" %-6s %-6s %-6s %-6s %-6s\n", "fork", "cs", "kill", "ps", "mount");
	printf(" %-6s\n\n", "umount");
	printf("************************************\n");
	printf(" Console Commands:\n");
	printf("************************************\n");
	printf(" %-6s %-6s\n\n", "help", "exit");
	return 1;
}

int invalid_command(char* inputLine)
{
	if (strlen(inputLine) > 0)
	{
		char* badCommand = strtok(inputLine, " \n");
		printf("Invalid command, \"%s\" is not defined\n", badCommand);
		printf("Type \"help\" to see a list of available commands\n\n");
	}
	return 1;
}

int close_program(char* notUsed)
{
	int i;
	SUPER super;
	
	/* Set running flag for program to exit */
	running = false;
	
	/* Go back to root directory */
	do_cd("");
	
	/* Close all files */
	printf("Closing files... ");
	for (i = 0; i < NUM_FILE_DESC; i++)
		while (openFiles[i].refCount > 0)
			close_file(i);
	printf("Done!\n");
	
	/* Write all inodes back to disk */
	printf("Writing back inodes... ");
	for (i = 0; i < NUM_MEM_INODES; i++)
		while (memInodes[i].refCount > 0)
			put_inode(&(memInodes[i]));
	printf("Done!\n");
			
	/* Closing open mounts */
	printf("Closing open mounts... ");
	for (i = NUM_MOUNTS; i > 0; i--)
		if (mountTable[i].mountedInode != 0)
			umount_device(mountTable[i].mountedName);			
	printf("Done!\n\n");
	
	/* Get root super block to print summary */
	get_super(mountTable[ROOT_DEVICE].handle, &super);
	
	/* Print root device summary */	
	printf("Root device summary:\n");
	printf(" Num blocks: %-10d  Num inodes: %-10d\n", mountTable[ROOT_DEVICE].diskInfo.numBlocks, mountTable[0].diskInfo.numInodes);	
	printf("Free blocks: %-10d Free inodes: %-10d\n\n", super.s_free_blocks_count, super.s_free_inodes_count);
	
	/* Close root device */
	printf("Closing root device... ");
	close(mountTable[ROOT_DEVICE].handle);
	printf("Done!\n");
}
